/*
 * @Description: cesium加载视图和基本处理的函数封装
 * @Author: your name
 * @version:
 * @Date: 2024-05-08 14:05:51
 * @LastEditors: your name
 * @LastEditTime: 2024-05-08 14:51:55
 */
import * as Cesium from 'cesium';
import { TencentImageryProvider } from './mapPlugin';
let tilesets, waterPrimitive;

// 初始化地图场景
export const initViewer = container => {
  // 初始化cesium地图对象viewer
  const viewer = new Cesium.Viewer(container, {
    timeline: true, // 是否显示时间线控件
    animation: false, // 是否显示动画控件
    baseLayerPicker: false, // 避免token
    infoBox: false, // 是否显示点击要素之后显示的信息
    selectionIndicator: false, //选中元素显示,默认true
    skyAtmosphere: false, //关闭地球光环
    homeButton: false,
    fullscreenButton: false,
    geocoder: false,
    sceneModePicker: false,
    shouldAnimate: true,
    navigationHelpButton: false,
    // 指定上下文
    contextOptions: {
      // 支持使用老版本webgl进行着色器编写
      requestWebgl1: true,
      // 开启纹理采样异性过滤，不开的话，视角越倾斜，越模糊
      allowTextureFilterAnisotropic: true,
      webgl: {
        alpha: false,
        depth: true,
        stencil: false,
        antialias: true,
        powerPreference: 'high-performance',
        premultipliedAlpha: true,
        preserveDrawingBuffer: false,
        failIfMajorPerformanceCaveat: false,
      },
    },
  });

  const options = {
    style: 4, //style: img、1：经典
    crs: 'WGS84',
  };
  // 添加腾讯底图
  viewer.imageryLayers.add(
    new Cesium.ImageryLayer(new TencentImageryProvider(options))
  );
  return viewer;
};

// 设置后处理效果
export const setScene = viewer => {
  //隐藏太阳
  viewer.scene.sun.show = false;
  //隐藏月亮
  viewer.scene.moon.show = false;
  viewer.scene.globe.enableLighting = true; // 开启全球光照

  // 泛光设置
  const bloom = viewer.scene.postProcessStages.bloom;
  bloom.enabled = false;

  // 亮度设置
  // 亮度设置
  var stages = viewer.scene.postProcessStages;
  viewer.postProcessStages.fxaa.enabled = true;
  viewer.scene.brightness =
    viewer.scene.brightness ||
    stages.add(Cesium.PostProcessStageLibrary.createBrightnessStage());
  viewer.scene.brightness.enabled = true;
  viewer.scene.brightness.uniforms.brightness = 1.2;
  viewer.scene.globe.depthTestAgainstTerrain = true;
  viewer._cesiumWidget._creditContainer.style.display = 'none';

  viewer.shadows = false; //开启阴影效果
  viewer.shadowMap.darkness = 0.8; //阴影透明度--越大越透明
  //去cesium logo水印 或 css
  viewer.cesiumWidget.creditContainer.style.display = 'none'; //去cesium logo水印

  // 场景设置
  viewer.scene.highDynamicRange = true;
  viewer.scene.postProcessStages.bloom.enabled = true;
  viewer.scene.postProcessStages.bloom.uniforms.contrast = 255;
  viewer.scene.postProcessStages.bloom.uniforms.brightness = 0.02;
  viewer.scene.postProcessStages.bloom.uniforms.glowOnly = false;
  viewer.scene.postProcessStages.bloom.uniforms.delta = 3.1;
  viewer.scene.postProcessStages.bloom.uniforms.sigma = 5;
  viewer.scene.postProcessStages.bloom.uniforms.stepSize = 0.6;
  viewer.scene.postProcessStages.bloom.uniforms.isSelected = false;
  viewer.scene.postProcessStages.bloom.uniforms.selectedBloom = 10;
  viewer.scene.postProcessStages.bloom.uniforms.bloomColor =
    Cesium.Color.fromCssColorString('#fafafa');
  viewer.scene.sun.show = false;
  viewer.scene.moon.show = false;
  viewer.scene.undergroundMode = false;
  viewer.scene.terrainProvider.isCreateSkirt = false;
  viewer.scene.globe.showGroundAtmosphere = false;
  viewer.scene.globe.enableLighting = false;
  viewer.scene.fog.enabled = false;
};

//时间线变化引发事件
export const handleUpdateScene=(viewer,tileset,waterPrimitive)=>{
  let _angle = 0;
  // 更新场景
  const updateScene = () =>{
    // 从世界坐标原点到太阳的单位向量
    const sunDirection = Cesium.Cartesian3.normalize(viewer.scene.sun._boundingVolume.center, new Cesium.Cartesian3);
    // 世界坐标到相机的单位向量
    const cameraDirection = Cesium.Cartesian3.normalize(viewer.camera.position, new Cesium.Cartesian3);
    // 点乘相机到太阳光两个向量，确定夹角,如果夹角余弦小于0，说明到了晚上，渲染夜间效果
    const angle = parseFloat(Cesium.Cartesian3.dot(cameraDirection, sunDirection).toFixed(3))
    //角度没变化，说明时间也没变，不更新
		if (angle === _angle) return false;

		if(angle>=0){
			//白天的逻辑，取消bloom等后处理效果
		}
		if(angle<0){
			// 夜晚的逻辑，开启bloom,并设置customShader和water
		}
    _angle=angle
  }
  viewer.scene.postRender.addEventListener(()=>updateScene())
}

// 多个3dtiles模型加载
// urls 多个3dtiles模型地址  loadCb 加载完成的回调
// urls<{url,options}>
export const loadTilesets = async (viewer, urls, loadCb) => {
  tilesets = urls.map(item => {
    const { url, options } = item;
    let params = { url };

    if (typeof options === 'object') {
      Object.assign(params, options);
    }
    const tile = viewer.scene.primitives.add(
      new Cesium.Cesium3DTileset(params)
    );
    return tile.readyPromise;
  });
  let result = await Promise.all(tilesets);
  loadCb && loadCb(result);
};

//水域加载
export const loadWaterJson = async (viewer, url) => {
  try {
    // 加载水域JSON数据
    const dataSource = await new Cesium.GeoJsonDataSource.load(url);
    // 这里获取到的数据是一个实体的dataSource,GeoJsonDataSource会自动将geojson编译为实体
    let entities = dataSource.entities.values;
    //console.log('水域数据', entities);
    const instances = [];
    entities.forEach(entity => {
      //console.log('转换为笛卡尔坐标的水域坐标',entity.polygon.hierarchy.getValue().positions)
      let geometry = new Cesium.GeometryInstance({
        geometry: new Cesium.PolygonGeometry({
          polygonHierarchy: new Cesium.PolygonHierarchy(
            entity.polygon.hierarchy.getValue().positions
          ),
          extrudedHeight: 0,
          height: 0,
          vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT,
        }),
      });
      instances.push(geometry);
    });
    // 水一般加载primitve,所以拿到坐标之后，直接构造instance
    const appearance = new Cesium.EllipsoidSurfaceAppearance({
      material: new Cesium.Material({
        fabric: {
          type: 'Water',
          uniforms: {
            // 在这里定义所有需要的uniform变量，例如：
            baseWaterColor: new Cesium.Color(
              64 / 255.0,
              157 / 255.0,
              253 / 255.0,
              0.5
            ),
            normalMap: '/src/assets/materialResources/waterNormals.jpg', //不要用new Cesium.Texture()包裹,会报错
            //specularMap: '/src/assets/materialResources/wallgradients.png', // 高光贴图路径//使用高光贴图,会出现半截无波纹,半截有波纹的情况
            specularMap: '',
            frequency: 1000.0, // 波频率
            animationSpeed: 0.1, // 动画速度
            amplitude: 10, // 振幅
            specularIntensity: 10.0, // 高光强度
            fadeFactor: 0.01, // 衰减因子
            blendColor: new Cesium.Color(0.0, 0.0), // 混合颜色
            u_isDark: false, // 初始值为白天
          },
          source: /*glsl*/ `
          #define TAU 6.28318530718
          #define MAX_ITER 5

          uniform sampler2D specularMap;
          uniform sampler2D normalMap;
          uniform vec4 baseWaterColor;
          uniform vec4 blendColor;
          uniform float frequency;
          uniform float animationSpeed;
          uniform float amplitude;
          uniform float specularIntensity;
          uniform float fadeFactor;
          uniform bool u_isDark;//这里不能写bloon
          struct Water{
            vec3 waterCol;
            float alpha;
            vec3 normal;
            float specular;
            float shininess;
          };//类型使用前必须要定义好⭐
          //第一种水面的材质 cesium源码中的water.glsl
          Water getWaterNormalCol(czm_materialInput materialInput){
            
            Water water;
            float time = czm_frameNumber * animationSpeed;
            // fade is a function of the distance from the fragment and the frequency of the waves// 计算衰减因子
            float fade = max(1.0, (length(materialInput.positionToEyeEC) / 10000000000.0) * frequency * fadeFactor);
            // 高光贴图采样
            float specularMapValue = texture2D(specularMap, materialInput.st).r;
            // note: not using directional motion at this time, just set the angle to 0.0;// 获取噪声
            vec4 noise = czm_getWaterNoise(normalMap, materialInput.st * frequency, time, 0.0);
            vec3 normalTangentSpace = noise.xyz * vec3(1.0, 1.0, (1.0 / amplitude));
            // fade out the normal perturbation as we move further from the water surface// 应用衰减
            normalTangentSpace.xy /= fade;
            // attempt to fade out the normal perturbation as we approach non water areas (low specular map value)
            normalTangentSpace = mix(vec3(0.0, 0.0, 50.0), normalTangentSpace, specularMapValue);
          
            normalTangentSpace = normalize(normalTangentSpace);// 归一化法线
          
            // get ratios for alignment of the new normal vector with a vector perpendicular to the tangent plane// 计算法线扰动比率
            float tsPerturbationRatio = clamp(dot(normalTangentSpace, vec3(0.0, 0.0, 1.0)), 0.0, 1.0);
            // 计算颜色和透明度
            float alpha=mix(blendColor.a, baseWaterColor.a, specularMapValue) * specularMapValue;
            vec3 col=mix(blendColor.rgb, baseWaterColor.rgb, specularMapValue);
            col+= (0.1 * tsPerturbationRatio);
             // 设置水属性
            water.waterCol=col;
            water.alpha=alpha;
            water.normal= normalize(materialInput.tangentToEyeMatrix * normalTangentSpace);
            water.specular=specularIntensity;
            water.shininess=10.0;
            return water;
          }
          // 第二种水面的材质设置:搬运自shaderToy
          vec3 getNightWaterCol(czm_materialInput materialInput){
            vec2 uv=materialInput.st;
            uv*=5.0;
            #ifdef SHOW_TILING
            vec2 p = mod(uv*TAU*2.0, TAU)-250.0;
            #else
            vec2 p = mod(uv*TAU, TAU)-250.0;
            #endif
            vec2 i = vec2(p);
            float c = 1.0;
            float inten = .005;
            float iTime = czm_frameNumber * animationSpeed;
            float time = iTime * .5+23.0;
            for (int n = 0; n < MAX_ITER; n++) 
            {
              float t = time * (1.0 - (3.5 / float(n+1)));
              i = p + vec2(cos(t - i.x) + sin(t + i.y), sin(t - i.y) + cos(t + i.x));
              c += 1.0/length(vec2(p.x / (sin(i.x+t)/inten),p.y / (cos(i.y+t)/inten)));
            }
            c /= float(MAX_ITER);
            c = 1.17-pow(c, 1.4);
            vec3 colour = vec3(pow(abs(c), 8.0));
            colour = clamp(colour + vec3(0.0, 0.35, 0.5), 0.0, 1.0);

            #ifdef SHOW_TILING
            // Flash tile borders...
            vec2 pixel = 2.0 / gl_FragCoord.xy;
            uv *= 2.0;
            float f = floor(mod(iTime*.5, 2.0)); 	// Flash value.
            vec2 first = step(pixel, uv) * f;		   	// Rule out first screen pixels and flash.
            uv  = step(fract(uv), pixel);				// Add one line of pixels per tile.
            colour = mix(colour, vec3(1.0, 1.0, 0.0), (uv.x + uv.y) * first.x * first.y); // Yellow line
            #endif
            return colour;
          }
          czm_material czm_getMaterial(czm_materialInput materialInput)
          {
            czm_material material = czm_getDefaultMaterial(materialInput);
            // 获取cesium官方提供的水面材质
            Water water=getWaterNormalCol(materialInput);//函数使用前必须要先定义好⭐
            vec3 waterNight=getNightWaterCol(materialInput);
            if(u_isDark){
              material.diffuse = waterNight;
              material.alpha=1.0;
            }else{
              // fade out water effect as specular map value decreases
              material.alpha = water.alpha;
              material.diffuse=water.waterCol;
              material.normal =water.normal;
              material.specular = water.specular;
              material.shininess = water.shininess;
            }
            return material;
          }
          `,
        },
      }),
    });
    waterPrimitive = new Cesium.GroundPrimitive({
      geometryInstances: instances, //合并
      //某些外观允许每个几何图形实例分别指定某个属性
      appearance: appearance,
    });
    viewer.scene.primitives.add(waterPrimitive);
  } catch (err) {
    console.error('Error loading water JSON:', err);
  }
};

// 处理模型展示效果
export const handleDefaultModelEffect = tile => {
  // 处理模型着色
  tile.style = new Cesium.Cesium3DTileStyle({
    color: {
      conditions: [
        ['Number(${Elevation})<20', "color('rgb(25, 211, 226)',0.0)"],

        ['Number(${Elevation})>20', "color('rgb(25, 211, 226)',1)"],
        //['true', "color('rgb(42, 122, 237)',1)"]
      ],
      show: false,
    },
  });
  const customShader = new Cesium.CustomShader({
    uniforms: {
      maxHeight: {
        type: Cesium.UniformType.FLOAT,
        value: 660.0,
      },
      minHeight: {
        type: Cesium.UniformType.FLOAT,
        value: 520.0,
      },
      u_IBLTexture: {
        type: Cesium.UniformType.SAMPLER_2D,
        value: new Cesium.TextureUniform({
          url: '/src/assets/materialResources/pic.jpg',
        }),
      },
      u_TextureNight: {
        type: Cesium.UniformType.SAMPLER_2D,
        value: new Cesium.TextureUniform({
          url: '/src/assets/materialResources/夜晚ibl反射贴图.jpg',
        }),
      },
      u_ColorTexture: {
        type: Cesium.UniformType.SAMPLER_2D,
        value: new Cesium.TextureUniform({
          url: '/src/assets/materialResources/color2.png',
        }),
      },
      u_isDark: {
        type: Cesium.UniformType.BOOL,
        value: false,
      },
    },
    mode: Cesium.CustomShaderMode.MODIFY_MATERIAL,
    lightingModel: Cesium.LightingModel.PBR,
    vertexShaderText: /*glsl*/ `
            void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput) {
            }
       `,
    fragmentShaderText: /*glsl*/ `
            //白天效果  
            vec3 getIBLReflection(vec3 positionEC,vec3 normalEC){
              // 在Cesium中，只需要positionEC
              vec3 eyeToSurfaceDir=normalize(positionEC);
              // 生成一个反射向量
              vec3 direction=reflect(eyeToSurfaceDir,normalEC);
              vec3 coord = normalize(vec3(czm_inverseViewRotation * direction));
              vec3 resCol=vec3(0.);
              // 如果是夜晚，使用夜间的反射贴图
              if(u_isDark){
                //在 GLSL 中，texture2D 已经被弃用，应该使用 texture 函数进行纹理采样
                resCol=texture2D(u_TextureNight,vec2(coord.x, (coord.z - coord.y) / 3.0)).rgb;//这里未用反射贴图
              }else{
                resCol=texture2D(u_IBLTexture,vec2(coord.x, (coord.z - coord.y) / 3.0)).rgb;
  
              }
              return resCol;
            }
            // 夜晚效果
            // 夜景下，再添加光带特效
            //接受两个参数：一个是模型坐标系中的位置向量 positionMC（类型为 vec3），另一个是表示高度影响因子的浮点数 czm_h
              vec3 getColorStripe(vec3 positionMC,float czm_h){
                //初始化一个颜色变量 col，设置为一个较暗的青绿色。这个颜色将作为条纹的基础颜色，除非被高亮覆盖。
                vec3 col=vec3(0.1,0.2,0.2);
                //定义一个 heightInterval 变量，用于指定条纹之间的间隔高度，这里是每50个单位一个条纹。
                float heightInterval=50.;//这里写成10.会有类似滚动光带的效果
                //定义 lineWidth 变量，指定每个条纹的宽度，这里是5个单位宽。
                float lineWidth=5.;
                // 对z轴坐标和高度取模，这样能实现条纹
                //计算当前位置的高度 positionMC.y 对 heightInterval 取模的结果 modRes。这将决定当前像素是否位于条纹区域内。如果 modRes 小于 lineWidth，则该像素属于条纹的一部分
                float modRes=mod(positionMC.y,heightInterval);
                //使用 Cesium 提供的 czm_frameNumber（当前帧号）除以120来模拟时间流逝，创建一个随时间变化的时间变量 iTime。这可以用来制作动画效果，比如移动的光带
                float iTime=czm_frameNumber/60.;
                // 构造高亮块的颜色
                // 构造uv坐标，其中v坐标需要给一个时间，不断取颜色
                //计算 textureX，它是通过将 positionMC.x 分割成50个单位长度片段并取小数部分得到的 UV 坐标分量。这确保了纹理在 X 方向上重复
                float textureX=fract(positionMC.x/50.);
                //计算 textureY，同样地，将 positionMC.y 分割成50个单位长度片段并取小数部分，然后减去 iTime 来创建一个随时间变化的 Y 轴 UV 坐标分量。这将导致纹理沿 Y 轴方向随着时间移动。
                float textureY=fract(positionMC.y/50.)-iTime;
                //从 u_ColorTexture 纹理中采样颜色，使用之前计算出的 textureX 和 textureY 作为 UV 坐标，并提取 RGB 颜色通道到 highLightCol 变量中。这里假设 u_ColorTexture 是一个已经定义好的纹理 uniform
                vec3 highLightCol=texture2D(u_ColorTexture,vec2(textureX,textureY)).rgb;
                // modRes小于lineWidth的时候，就说明要高亮
                //检查 modRes 是否小于 lineWidth。如果是，则将 col 设置为从纹理中采样的高亮颜色 highLightCol；否则，将 col 乘以 czm_h，即根据高度调整颜色亮度。
                if(modRes<lineWidth){
                  col=highLightCol;
                }else{
                  col*=czm_h;
                }
                return col;
              }
               
            void fragmentMain(FragmentInput fsInput,inout czm_modelMaterial material) {
                vec3 positionMC = fsInput.attributes.positionMC;
                 // 获取眼睛坐标系下的法向量
                vec3 normalEC=fsInput.attributes.normalEC;
                vec3 positionEC=fsInput.attributes.positionEC;
                // 注意shader中写浮点数是 一定要带小数点 否则会报错 比如0需要写成0.0 1要写成1.0
                float _baseHeight = minHeight; // 物体的基础高度，需要修改成一个合适的建筑基础高度
                float _heightRange = maxHeight; // 高亮的范围(_baseHeight ~ _baseHeight + _heightRange)
                //float _glowRange = maxHeight; // 光环的移动范围(高度)

                float czm_height = clamp(0.0,1.0,(positionMC.y-_baseHeight/_heightRange));//这里不能用语雀代码
                 // 计算基础颜色
                float baseColor = clamp(czm_height / _heightRange, 0.0, 1.0);
                float bottomColor = clamp(baseColor * 2.0, 0.0, 1.0);
                // 房顶给固定颜色增加立体感
                // if(czm_height<29.0) {
                //     material.diffuse *= vec3(bottomColor);
                // }else{
                //   material.diffuse *= vec3(baseColor); // 渐变
                // }
                vec3 resCol;
                resCol *= vec3(baseColor); // 渐变
                
                // // 获取环境反射颜色
                vec3 reflectionColor = getIBLReflection(positionEC, normalEC);
                resCol += reflectionColor;
                

                // 添加光带特效
                vec3 stripeColor = getColorStripe(positionMC, czm_height);
                material.diffuse = stripeColor+resCol;

            }
         `,
  });
  tile.customShader = customShader;
};

// 视角飞到全局
export const flyToDefaultView = viewer => {
  viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(113.95, 30.19, 34000),
    duration: 2,
    orientation: {
      heading: Cesium.Math.toRadians(35.0),
      pitch: Cesium.Math.toRadians(-37.0),
      roll: 0.0,
    },
  });
};
